package org.sean.framework.util;


import okhttp3.ConnectionPool;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.sean.framework.logging.Logger;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;

/**
 * Http 工具类
 *
 * @author Xielei
 * @see cn.hutool.http.HttpUtil
 */
public final class HttpUtil {
    public static final String CONTENT_TYPE_FORM = "application/x-www-form-urlencoded; charset=utf-8";
    private static final int READ_TIME_OUT = 30;
    private static final int CONNECT_TIME_OUT = 5;
    private static final int MAX_IDLE_CONNECTS = 20;
    private static OkHttpClient okHttpClient;
    private static Logger logger = Logger.newInstance(HttpUtil.class);

    static {
        okHttpClient = new OkHttpClient.Builder().readTimeout(READ_TIME_OUT, TimeUnit.SECONDS)
                .connectTimeout(CONNECT_TIME_OUT, TimeUnit.SECONDS)
                .connectionPool(new ConnectionPool(MAX_IDLE_CONNECTS, 5, TimeUnit.SECONDS))
                .build();
    }


    private HttpUtil() {
    }

    /**
     * GET 请求
     *
     * @param url url
     * @return 响应数据
     */
    public static byte[] get(String url) {
        return executeForBytes("GET", url, null, null);
    }

    /**
     * GET 请求
     *
     * @param url     url
     * @param headers headers
     * @return 响应数据
     */
    public static byte[] get(String url, Headers headers) {
        return executeForBytes("GET", url, headers, null);
    }

    /**
     * GET 请求
     *
     * @param <T> T
     * @param url url
     * @param cls Class
     * @return 实体
     */
    public static <T> T getForObject(String url, Class<T> cls) {
        return executeForObject("GET", url, null, null, cls);
    }

    /**
     * GET 请求
     *
     * @param <T>     T
     * @param url     url
     * @param headers headers
     * @param cls     Class
     * @return 实体
     */
    public static <T> T getForObject(String url, Headers headers, Class<T> cls) {
        return executeForObject("GET", url, headers, null, cls);
    }

    /**
     * GET 请求
     *
     * @param <T>     T
     * @param url     url
     * @param headers headers
     * @param type    GSONType
     * @return 实体
     */
    public static <T> T getForObject(String url, Headers headers, GSONType<T> type) {
        return executeForObject("GET", url, headers, null, type);
    }

    /**
     * POST 请求
     *
     * @param url     url
     * @param headers headers
     * @param body    参数
     * @return 响应数据
     */
    public static byte[] post(String url, Headers headers, RequestBody body) {
        return executeForBytes("POST", url, headers, body);
    }

    /**
     * POST 请求
     *
     * @param <T>     T
     * @param url     url
     * @param headers headers
     * @param body    参数
     * @param type    GSONType
     * @return 实体
     */
    public static <T> T postForObject(String url, Headers headers, RequestBody body, GSONType<T> type) {
        return executeForObject("POST", url, headers, body, type);
    }

    /**
     * POST 请求
     *
     * @param <T>     T
     * @param url     url
     * @param headers headers
     * @param body    参数
     * @param cls     Class
     * @return 实体
     */
    public static <T> T postForObject(String url, Headers headers, RequestBody body, Class<T> cls) {
        return executeForObject("POST", url, headers, body, cls);
    }

    /**
     * PUT 请求
     *
     * @param <T>     T
     * @param url     url
     * @param headers headers
     * @param body    参数
     * @param type    GSONType
     * @return 实体
     */
    public static <T> T putForObject(String url, Headers headers, RequestBody body, GSONType<T> type) {
        return executeForObject("PUT", url, headers, body, type);
    }

    /**
     * PUT 请求
     *
     * @param <T>     T
     * @param url     url
     * @param headers headers
     * @param body    参数
     * @param cls     Class
     * @return 实体
     */
    public static <T> T putForObject(String url, Headers headers, RequestBody body, Class<T> cls) {
        return executeForObject("PUT", url, headers, body, cls);
    }

    /**
     * PATCH 请求
     *
     * @param <T>     T
     * @param url     url
     * @param headers headers
     * @param body    参数
     * @param type    GSONType
     * @return 实体
     */
    public static <T> T patchForObject(String url, Headers headers, RequestBody body, GSONType<T> type) {
        return executeForObject("PATCH", url, headers, body, type);
    }

    /**
     * PATCH 请求
     *
     * @param <T>     T
     * @param url     url
     * @param headers headers
     * @param body    参数
     * @param cls     Class
     * @return 实体
     */
    public static <T> T patchForObject(String url, Headers headers, RequestBody body, Class<T> cls) {
        return executeForObject("PATCH", url, headers, body, cls);
    }

    /**
     * DELETE 请求
     *
     * @param <T>     T
     * @param url     url
     * @param headers headers
     * @param type    GSONType
     * @return 实体
     */
    public static <T> T delete(String url, Headers headers, GSONType<T> type) {
        return executeForObject("DELETE", url, headers, null, type);
    }

    /**
     * DELETE 请求
     *
     * @param <T>     T
     * @param url     url
     * @param headers headers
     * @param cls     Class
     * @return 实体
     */
    public static <T> T delete(String url, Headers headers, Class<T> cls) {
        return executeForObject("DELETE", url, headers, null, cls);
    }

    /**
     * 发生请求
     *
     * @param <T>     T
     * @param method  方法
     * @param url     url
     * @param headers 请求头
     * @param body    内容
     * @param type    实体类型
     * @return 响应数据
     */
    public static <T> T executeForObject(String method, String url, Headers headers, RequestBody body, GSONType<T> type) {
        try {
            byte[] responseBody = executeForBytes(method, url, headers, body);
            if (responseBody != null) {
                return GSONUtil.json2Obj(new String(responseBody, Charset.defaultCharset()), type);
            }
        } catch (Exception e) {
            logger.printStackTrace(e);
        }
        return null;
    }

    /**
     * 执行网络请求
     *
     * @param <T>     T
     * @param method  方法类型
     * @param url     URL
     * @param headers 请求头
     * @param body    请求体
     * @param cls     类型
     * @return 响应数据
     **/
    public static <T> T executeForObject(String method, String url, Headers headers, RequestBody body, Class<T> cls) {
        try {
            byte[] responseBody = executeForBytes(method, url, headers, body);
            if (responseBody != null) {
                return GSONUtil.json2Obj(new String(responseBody, Charset.defaultCharset()), cls);
            }
        } catch (Exception e) {
            logger.printStackTrace(e);
        }
        return null;
    }

    /**
     * 执行网络请求
     *
     * @param method  方法类型
     * @param url     URL
     * @param headers 请求头
     * @param body    请求体
     * @return 响应数据
     */
    public static byte[] executeForBytes(String method, String url, Headers headers, RequestBody body) {
        Request.Builder requestBuild = new Request.Builder()
                .url(url)
                .method(method, body);
        if (headers != null) {
            requestBuild.headers(headers);
        }

        try (Response response = okHttpClient.newCall(requestBuild.build()).execute()) {
            InputStream is = null;
            if (response.isSuccessful()) {
                ResponseBody responseBody = response.body();
                if (responseBody != null) {
                    is = responseBody.byteStream();
                }
            }
            if (is != null) {
                try {
                    return IOUtil.readBytes(is);
                } finally {
                    try {
                        is.close();
                    } catch (Exception e) {
                        // do nothing
                    }
                }
            }
        } catch (IOException e) {
            logger.printWarnStackTrace(e);
        }
        return null;
    }
}
